#include "main.h"
#include "Temp_Control.h"
#include "Temp_Table.h"

xdata pid_Struct_t pid;
xdata uint8_t	pid_self_first_status_flag = 0;						/* 记录自整定初始状态 0 设定温度 低于 当前温度  //1设定温度 高于 或者 等于  当前温度  启动加热 */
xdata uint8_t	Step_Auto = 0;										/* 零点判断值 */
xdata uint8_t	PID_read = 0;										/* 初始状态读取判断值 */
xdata uint8_t	PID_Auto_Deal = 0;									/* 自整定处理标志位 */
xdata uint32_t	PID_Auto_Time = 0;									/* 整定时间计数 */
xdata uint8_t	zero_across_counter = 0;							/* 温度自整定振荡零点个数 */
xdata uint16_t	time_low = 0;										/* 振荡周期低时间 */
xdata uint16_t	time_high = 0;										/* 振荡周期高时间 */
xdata uint16_t 	pid_self_calc_buffer[4] = {0,0,0,0};				/* 温度计算数组 */
xdata uint8_t	k_pid_self_counter = 0;								/* 温度计算数组索引 */
xdata uint8_t	enable_calc_min_max_flag = 0;						/* 数组存满标志位 */
float temp_low = 400 , temp_high = -50;
//xdata uint8_t	PID_Auto_flag = 0;									/* 整定完成后，需延时散热一段时间 */

/*******************************************************
*函 数 名：TableLookup
*参    数：usVoltage 电压值
           usDataLength 表长度
*返 回 值：查表得到的温度
*作    用：热电偶温度查表
********************************************************/
u16 TableLookup(u16 usVoltage,u16 usDataLength,u16 *usTempBuf)
{
	u16 usTemp = 0;
	u16 mid = 0;
	u16 usLow = 0;
	u16 usHigh = usDataLength - 1;
	while (usLow <= usHigh)
	{
		mid = usLow + (usHigh - usLow) / 2;
        if (usTempBuf[mid] <= usVoltage) 
		{
            if(usTempBuf[mid+1] > usVoltage)
			{
				usTemp = 10 * (mid +((float)usVoltage - usTempBuf[mid])/(usTempBuf[mid+1] - usTempBuf[mid]));
//				usTemp = mid;
				return usTemp;
			}
			
			else
				usLow = mid + 1;				
        } 
		else 
		{
            usHigh = mid - 1; 
        }
    }
	return 0;
}

/**
  * @brief  adc与温度拟合函数，通过python拟合
  * @param  adc
  * @retval 温度值
  */
float temp_Sample(uint16_t 	adc_value)
{
//	if(adc_value <= 607)
//	{
//		return (0.1813 * adc_value - 83.37)  + SC;
//	}
//	if(adc_value > 607 && adc_value <= 620)
//	{
//		return (0.5 * adc_value - 279) + SC;
//	}
//	else if(adc_value > 620 && adc_value <= 940)
//	{
//		return (0.1873 * adc_value - 84.73) + SC;
//	}
//	else if(adc_value > 940)
//	{
//		return (0.1483 * adc_value - 48.53) + SC;
//	}
//	else
//	{
//		return 0;
//	}
	float voltage1000 = 0;
	u16 Voltage = 0;
	voltage1000 = ((float)adc_value/4095)*5*1000;
	Voltage = (float)(voltage1000 - POINTVOTAGE)/166*1000;
	return TableLookup(Voltage, 400, GusTempBuf)/10 + SC;
//	return Voltage;
}

/**
  * @brief  adc滤波函数，中位数滤波
  * @param  无
  * @retval 滤波adc值
  */
#define N 99
uint16_t middleValueFilter(void)
{
    xdata uint16_t value_buf[N];
    xdata uint16_t i,j,k,temp;
	static xdata uint16_t adc_result_now;
    for( i = 0; i < N; ++i)
    {
        value_buf[i] = ADC_Sample_TC();
    }
    for(j = 0 ; j < N-1; ++j)
    {
        for(k = 0; k < N-j-1; ++k)
        {
            //从小到大排序，冒泡法排序
            if(value_buf[k] > value_buf[k+1])
            {
                temp = value_buf[k];
                value_buf[k] = value_buf[k+1];
                value_buf[k+1] = temp;
            }
        }
    }
//	adc_result_last = adc_result_now;
	adc_result_now = value_buf[(N - 1)/2];
//    return adc_result_last * 0.3 + adc_result_now * 0.7;
	return adc_result_now;
}

void PID_Init(void)
{
	pid.Sv = 40;
	pid.Kp = 50;
	pid.T_Calc = /*pid.pwmcycle +*/ 1*100;
	pid.Ki = 33.3;
	pid.Kd = 13;
	pid.pwmcycle = 3*100;					//单位10ms,主菜单参数
	pid.PID_OUT0 = 0;
	pid.EkLast = 0;
	pid.SEk = 0;
}

void ReadTemp(void)
{
	GusADC_Result = middleValueFilter();
	pid.Pv  = temp_Sample(GusADC_Result);
}

void PID_Calculate(void)
{
	xdata float delEk;
	xdata int32_t Iout,Pout,Dout;
	xdata float out;
	
	if(pid.C10ms<pid.T_Calc)
	{
		return ;
	}
	ReadTemp();
	
	pid.EkLast = pid.EkNow;
	pid.EkNow = (pid.Sv - pid.Pv);
	if(pid.EkNow <= 3&&pid.EkNow >= -3)		//当温度差绝对值小于3 
	{
		pid.SEk += pid.EkNow;; //计算累计误差
		
	}
	else if(pid.EkNow >= 3&&pid.EkNow <= -3)		//当温度差绝对值大于3 
	{
		pid.SEk = 0;
	}
	
	
	pid.SEk = pid.SEk > Ar?Ar:(pid.SEk<0?0:pid.SEk);
	delEk = pid.EkNow - pid.EkLast;
	
	Pout = (int32_t)pid.EkNow*pid.Kp/10;
	
	Iout = (int32_t)pid.SEk*(pid.Ki)/1000;
	
	Dout = (int32_t)pid.Kd*delEk/10;
 	
	out = Pout+Iout+Dout+pid.PID_OUT0;

	//百分比
	out = out > 100?100:(out<0?0:out);
	if(pid.Pv > pid.Sv+2)
	{
		out = 0;
	}
	pid.PID_OUT = (float)out/100;				//占空比
	
	//装载值·	
	pid.C10ms = 0;
}

void PID_out(void)
{
	static u32 pw;				//一个计数10ms
	pw++;
	if(pw >= pid.pwmcycle)
	{
		pw = 0;
	}
	//pwÔÚ  0~999
	
	if(pw < pid.PID_OUT*pid.pwmcycle)
	{
		PWM_OUT(1);
		Nixie_SetBuf_forcase9(1, OUT);
	}
	else
	{
		PWM_OUT(0);
		Nixie_SetBuf_forcase9(0, ~OUT);
	}
}

//PID参数保存函数
void PID_Save(void)
{
	pid.Sv = SV_MENU;
	pid.Kp = (float)PID_P;
	pid.Ki = (float)PID_I;
	pid.Kd = (float)PID_D;
	pid.pwmcycle = T*100;			//单位10ms
//	pid.T_Calc = pid.pwmcycle + 2*100;
}

/***********************************************************************/
//*******函数名:void PID_Auto(void)
//*******用途:PID自整定
//*******说明:构建闭环回路 确定稳定极限   确定两个参数  极限值KP和震荡周期·
//*******参数:无
//*******返回值:无  
/***********************************************************************/
void PID_Auto(void)
{
	static xdata uint8_t pid_heat_cnt = 0;
	static xdata uint8_t pid_cool_cnt = 0;
	
	xdata uint8_t i = 0;
	xdata float KC = 0;
	xdata float TC = 0;
	xdata float sum_temp = 0,min_temp = 0,max_temp = 0,aver_temp = 0;
//	static xdata int16_t temp_low = 400 , temp_high = -50;
	//第一步进入比较初始温度 确定此时温度处于哪种情况
	if(PID_read == 0)
	{
		if(pid.Sv >  pid.Pv)	//	如果初始温度小于设定值 
		{
			pid_self_first_status_flag = 1;
			Step_Auto = 0;
		}
		else//	如果初始温度大于设定值 
		{
			pid_self_first_status_flag = 0;
			Step_Auto = 1;
		}
		PID_read = 1;
	}
	if(PID_Auto_Deal == 1)	//自整定过程  修改成500ms 计算一次  如果需要精度很高 计算周期必须短 
	{
//		//PID_Auto_Deal = 0;	//清除标识
		PID_Auto_Time++;	//整定时间计数
		if(PID_Auto_Time >= 3600)//整定时间超过60分钟 整定失败数据回滚
		{
			PID_Auto_Time = 0;	//计数清零
		}
		//程序第一次进入 查看对比当前温度和设定温度
		//0 设定温度 低于 当前温度  //1设定温度 高于 或者 等于  当前温度  启动加热
		if(( pid_self_first_status_flag == 1) || ( pid_self_first_status_flag == 0))//0 设定温度 低于 当前温度  //1设定温度 高于 或者 等于  当前温度  启动加热
		{
			if(pid.Sv > pid.Pv)//启动加热
			{
				pid_cool_cnt = 0;
				pid_heat_cnt++;
				if(pid_heat_cnt >= 3)//连续3次结果都大于大于实际温度
				{
//					pid.PID_OUT = 1;//全速加热
					PWM_OUT(1);
					Nixie_SetBuf_forcase9(1, OUT);
					if(Step_Auto == 0)
					{
						Step_Auto = 1;
						zero_across_counter++;
						if(zero_across_counter == 3)
						{
							time_low = PID_Auto_Time - 3;//此时的时间不是最低温度对应的时间
						}
					}
				}
			}
			else//当前温度 大于 设定温度 停止加热
			{
				pid_cool_cnt++;
				pid_heat_cnt = 0;
				if(pid_cool_cnt > 3)
				{
//					pid.PID_OUT = 0;//不加热
					PWM_OUT(0);
					Nixie_SetBuf_forcase9(0, ~OUT);
					if(Step_Auto == 1)
					{
						Step_Auto = 0;
						zero_across_counter++;
						if(zero_across_counter == 3)
						{
							time_low = PID_Auto_Time - 3;//此时的时间不是最低温度对应的时间
						}
					}
				}
			}
		}
		//最低温度 出现在 zero_across_counter = 3 的阶段
		//最高温度 出现在 zero_across_counter = 4 的阶段
		if((zero_across_counter == 3 ) || (zero_across_counter == 4 ))
		{
			pid_self_calc_buffer[k_pid_self_counter] = pid.Pv;
			k_pid_self_counter++;
			if(k_pid_self_counter > 3)//0--3 共4个元素
			{
				k_pid_self_counter = 0;
				enable_calc_min_max_flag = 1;
			}
			if(enable_calc_min_max_flag == 1)//只要有4个值，就可以计算了 后面来的值覆盖了前面的值
			{
				//去掉最小值 最大值 取剩下2个值的平均值 
				sum_temp = 0;  //先清0
				min_temp = 400.0;
				max_temp = -80.0;
				for(i = 0;i < 4;i++)
				{
					if(pid_self_calc_buffer[i] <= min_temp)
					{
						min_temp = pid_self_calc_buffer[i];
					}
					if(pid_self_calc_buffer[i] >= max_temp)
					{
						max_temp = pid_self_calc_buffer[i];
					}
					sum_temp += pid_self_calc_buffer[i];
				}
				sum_temp =  sum_temp - min_temp - max_temp ;
				if(pid_self_first_status_flag == 1)
				{
					if(zero_across_counter == 3 )//最低温度
					{
						aver_temp = (sum_temp/2.0);					
						if( aver_temp <= temp_low )
						{
							temp_low = aver_temp;
						}				
					}
					else if(zero_across_counter == 4 )//最高温度
					{
						aver_temp = (sum_temp/2.0);
						if( aver_temp >= temp_high )
						{
							temp_high = aver_temp;
						}
					}
				}
				else if(pid_self_first_status_flag == 0)
				{
					if(zero_across_counter == 4 )//最低温度
					{
						aver_temp = (sum_temp/2.0);					
						if( aver_temp <= temp_low )
						{
							temp_low = aver_temp;
						}				
					}
					else if(zero_across_counter == 3 )//最高温度
					{
						aver_temp = (sum_temp/2.0);
						if( aver_temp >= temp_high )
						{
							temp_high = aver_temp;
						}
					}
				}
			}
		}
		else if(zero_across_counter == 5 )//4次过0 则说明出现了振荡 整定成功
		{
			zero_across_counter = 0;
			PID_Auto_Deal = 0;//退出pid阶段
			time_high = PID_Auto_Time - 3;//此时的时间不是最高温度对应的时间
			KC = 127/(temp_high - temp_low);
			TC = (time_high - time_low);//如果记录了 最低温度 与 最高温度对应的时间 那么沿用这个公式：TC = 2 * (TIME_Hight - TIME_LOW);
			pid.Kp = 0.6*KC*10;
			pid.Ki = (0.6*KC)/(0.5*TC)*1000;			/*PID.Ki = (0.6*KC)/(0.5*TC)/10;*/
			pid.Kd = (0.6*KC)*(0.125*TC)/6;
			PID_P = (int16_t)pid.Kp;
			PID_I = (int16_t)pid.Ki;
			PID_D = (int16_t)pid.Kd;
			PID_Save();
			temp_high = -50;	
			temp_low = 400;		 	 	
			time_high = 0;		 		
			time_low = 0;		 	 	
			PID_Auto_Time = 0;
			
			AT = 0;
			Nixie_SetBuf_forcase9(0, ~AT_S);
		}
	}
//	if(PID_Auto_Deal_judge() == 1)
//	{
//		PID_Auto_flag = 1;
//	}
//	if(PID_Auto_flag ==1)
//	{
//		PID_Auto_Time++;
//		pid.PID_OUT = 0;//不加热
//		PID_Calculate_flag = 0;//PID不计算
//		if(PID_Auto_Time == 300)
//		{
//			AT = 0;
//			Nixie_SetBuf_forcase9(0, ~AT_S);
//			pid.SEk = 0;
//			PID_Calculate_flag = 1;
//			PID_Auto_Time = 0;
//			PID_Auto_flag = 0;
//		}
//	}
}

//uint8_t PID_Auto_Deal_judge(void)
//{
//	static xdata Last_State = 0;
//	static xdata Now_State = 0;
//	Last_State = Now_State;
//	Now_State = PID_Auto_Deal;
//	if(Last_State == 1 && Now_State == 0)
//	{
//		return 1;
//	}
//	else
//	{
//		return 0;
//	}
//}
